package registration;

import java.sql.*;
import java.util.*;

import msg.*;
import user.*;

import common.Pair;
import common.dal.*;
import common.dal.SQLDataColumn.Constraints;
import common.exceptions.*;
import common.interfaces.IEventRegistration;

class DBEventRegister extends AbstractPersistent implements IEventRegistration {
	
	protected DBEventRegister(Transaction t) {
		super(t);
	}
	
	private final static String	USER	= "userId";
	private final static String	EVENT	= "eventId";
	
	private static Collection<WhereEntry> getWhereClause(User user, EventMessage event) {
		return Arrays.asList(new WhereEntry(USER, WhereOperator.EQUALS, user.getLogin()), new WhereEntry(EVENT,
				WhereOperator.EQUALS, event.getId()));
	}
	
	private static WhereEntry getWhereClause(User user) {
		return new WhereEntry(USER, WhereOperator.EQUALS, user.getLogin());
	}
	
	private static WhereEntry getWhereClause(EventMessage event) {
		return new WhereEntry(EVENT, WhereOperator.EQUALS, event.getId());
	}
	
	@Override
	protected SQLDataColumn[] getTableColumns() {
		return new SQLDataColumn[] {
				new SQLDataColumn(EVENT, SQLDataType.BIGINT,
						EnumSet.of(Constraints.FOREIGN_KEY, Constraints.NOT_NULL, Constraints.INDEX),
						Pair.of(DBMessagePersistencyManager.TABLE_NAME, DBMessagePersistencyManager.PRIMARY_KEY)),
				new SQLDataColumn(USER, SQLDataType.VARCHAR_50,
						EnumSet.of(Constraints.FOREIGN_KEY, Constraints.NOT_NULL, Constraints.INDEX),
						Pair.of(DBPasswordManager.TABLE_NAME, DBPasswordManager.PRIMARY_KEY))};
	}
	
	@Override
	protected String getTableName() {
		return "events";
	}
	
	@Override
	public boolean register(User user, EventMessage event) throws SQLException, InvalidEventException,
			InvalidUserException {
		if (user == null) {
			throw new IllegalArgumentException("user is null");
		}
		if (event == null) {
			throw new IllegalArgumentException("event is null");
		}
		
		if (isUserRegisteredToEvent(user, event)) {
			return false;
		}
		
		insertInto(new Object[] {event.getId(), user.getLogin()});
		return true;
	}
	
	@Override
	public boolean unregister(User user, EventMessage event) throws InvalidEventException, InvalidUserException,
			SQLException {
		if (user == null) {
			throw new IllegalArgumentException("user is null");
		}
		if (event == null) {
			throw new IllegalArgumentException("event is null");
		}
		
		if (isUserRegisteredToEvent(user, event) == false) {
			return false;
		}
		
		deleteWhere(getWhereClause(user, event));
		return true;
	}
	
	// private void removeAll(IDAL<IHasId<Key>, Key> persistent, IHasId<Key>
	// object, Exception e, String whereClause) throws SQLException, Exception {
	// if (object == null) {
	// throw new IllegalArgumentException("object is null");
	// }
	// if (persistent.exists(object.getId()) == false) {
	// throw e;
	// }
	//
	// execute("DELETE FROM " + getTableName() + whereClause);
	// }
	
	@Override
	public void removeAllUserRegisrations(User user) throws SQLException, InvalidUserException {
		if (user == null) {
			throw new IllegalArgumentException("user is null");
		}
		
		if (LoginManager.getNewInstance(t).exists(user.getLogin()) == false) {
			throw new InvalidUserException(user);
		}
		deleteWhere(getWhereClause(user));
	}
	
	@Override
	public void removeAllEventRegisrations(EventMessage event) throws SQLException, InvalidEventException {
		if (event == null) {
			throw new IllegalArgumentException("event is null");
		}
		
		if (MessageManager.getNewInstance(t).exists(event.getId()) == false) {
			throw new InvalidEventException(event);
		}
		deleteWhere(getWhereClause(event));
	}
	
	@Override
	public Collection<EventMessage> getAllEventRegisteredByUser(User user) throws SQLException,
			InvalidUserException {
		if (user == null) {
			throw new IllegalArgumentException("user is null");
		}
		
		if (LoginManager.getNewInstance(t).exists(user.getLogin()) == false) {
			throw new InvalidUserException(user);
		}
		try (ResultSet set = selectWhere(getWhereClause(user))) {
			Collection<EventMessage> result = new HashSet<>();
			while (set.next()) {
				long id = set.getLong(1);
				try {
					result.add((EventMessage)MessageManager.getNewInstance(t).get(id));
				} catch (InvalidKeyException e) {
					throw new AssertionError("event with id " + id + " was saved in " + getTableName()
							+ " but does not exists in foreign table", e);
				}
			}
			
			return result;
		}
	}
	
	@Override
	public Collection<User> getAllUsersRegisteredToEvent(EventMessage event) throws SQLException,
			InvalidEventException {
		if (event == null) {
			throw new IllegalArgumentException("event is null");
		}
		
		if (MessageManager.getNewInstance(t).exists(event.getId()) == false) {
			throw new InvalidEventException(event);
		}
		
		try (ResultSet set = selectWhere(getWhereClause(event))) {
			Collection<User> result = new HashSet<>();
			while (set.next()) {
				String id = set.getString(USER);
				try {
					result.add(LoginManager.getNewInstance(t).get(id));
				} catch (InvalidKeyException e) {
					throw new AssertionError("user with id " + id + " was registered in " + getTableName()
							+ " but does not exists in foreign table", e);
				}
			}
			
			return result;
		}
	}
	
	@Override
	public int getTotalRegisteredTo(EventMessage event) throws SQLException, InvalidEventException {
		if (event == null) {
			throw new IllegalArgumentException("event is null");
		}
		
		if (MessageManager.getNewInstance(t).exists(event.getId()) == false) {
			throw new InvalidEventException(event);
		}
		return countWhere(getWhereClause(event));
	}
	
	@Override
	public boolean isUserRegisteredToEvent(User user, EventMessage event) throws InvalidEventException,
			InvalidUserException, SQLException {
		if (event == null) {
			throw new IllegalArgumentException("event is null");
		}
		if (user == null) {
			throw new IllegalArgumentException("user is null");
		}
		if (LoginManager.getNewInstance(t).exists(user.getLogin()) == false) {
			throw new InvalidUserException(user);
		}
		
		if (MessageManager.getNewInstance(t).exists(event.getId()) == false) {
			throw new InvalidEventException(event);
		}
		return existsWhere(getWhereClause(user, event));
	}
}
